import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:mobile_app.core/collection_navigator/application/bloc/bloc.dart';
import 'package:mobile_app.core/collection_navigator/application/models/collection_single_lexical_item_page_model.dart';
import 'package:mobile_app.core/collection_navigator/application/models/single_collection_page_model.dart';
import 'package:mobile_app.core/core/application/models/app_navigator_transition_info.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/collection_list_item/collection_list_item.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/lexical_item/lexical_item.dart';
import 'package:mobile_app.core/core/domain/utils/either_extension.dart';
import 'package:mobile_app.core/core/domain/utils/with_previous_stream.dart';
import 'package:mobile_app.core/learning_content/application/bloc/bloc.dart';
import 'package:mobile_app.core/overlay/application/bloc/bloc.dart';
import 'package:mobile_app.core/purchase_flow_display/application/bloc.dart';
import 'package:mobile_app.core/root_navigator/application/bloc/bloc.dart';
import 'package:mobile_app.core/root_navigator/application/models/lexical_item_addition_page_model.dart';
import 'package:mobile_app.core/user_info/application/bloc/bloc.dart';
import 'package:mobile_app.home.collections/single_collection_page/application/models/collection_deletion_dialog_status_info.dart';
import 'package:mobile_app.home.collections/single_collection_page/application/models/collection_lexical_item_removal_dialog_status_info.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/entities/collection_deletion_mode.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/entities/collection_lexical_item_removal_mode.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/cancel_known_lexical_item_selection/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/cancel_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/complete_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/complete_custom_collection_renaming/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/complete_known_lexical_item_selection/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/complete_selected_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/deselect_all_lexical_items_during_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/get_initial_data/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_custom_collection_renaming_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_custom_collection_renaming_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_default_state_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_default_state_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_known_lexical_item_selection_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_lexical_item_removal_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/handle_lexical_item_removal_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/pronounce_lexical_item/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/select_all_lexical_items_during_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/start_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/start_known_lexical_item_selection/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/start_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/start_selected_lexical_item_removal/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/toggle_known_lexical_item_selection/use_case.dart';
import 'package:mobile_app.home.collections/single_collection_page/domain/use_cases/toggle_lexical_item_removal_selection/use_case.dart';
import 'package:mobile_app.home.collections.core/core/domain/helpers/validate_custom_collection_title/helper.dart';

part 'bloc_state.dart';

class SingleCollectionPageBloc extends Cubit<SingleCollectionPageBlocState> {
  SingleCollectionPageBloc({
    required SingleCollectionPageModel pageModel,
    required LearningContentBloc learningContentBloc,
    required UserInfoBloc userInfoBloc,
    required OverlayBloc overlayBloc,
    required RootNavigatorBloc rootNavigatorBloc,
    required CollectionNavigatorBloc collectionNavigatorBloc,
    required PurchaseFlowDisplayBloc purchaseFlowDisplayBloc,
    required GetInitialData getInitialData,
    required CancelKnownLexicalItemSelection cancelKnownLexicalItemSelection,
    required CancelLexicalItemRemoval cancelLexicalItemRemoval,
    required CompleteCustomCollectionRenaming completeCustomCollectionRenaming,
    required CompleteKnownLexicalItemSelection completeKnownLexicalItemSelection,
    required CompleteSelectedLexicalItemRemoval completeSelectedLexicalItemRemoval,
    required CompleteCollectionDeletion completeCollectionDeletion,
    required DeselectAllLexicalItemsDuringLexicalItemRemoval
        deselectAllLexicalItemsDuringLexicalItemRemoval,
    required HandleDefaultStateLearningContentOuterUpdate
        handleDefaultStateLearningContentOuterUpdate,
    required HandleDefaultStatePremiumAccessStatusOuterUpdate
        handleDefaultStatePremiumAccessStatusOuterUpdate,
    required HandleCustomCollectionRenamingLearningContentOuterUpdate
        handleCustomCollectionRenamingLearningContentOuterUpdate,
    required HandleCustomCollectionRenamingPremiumAccessStatusOuterUpdate
        handleCustomCollectionRenamingPremiumAccessStatusOuterUpdate,
    required HandleKnownLexicalItemSelectionLearningContentOuterUpdate
        handleKnownLexicalItemSelectionLearningContentOuterUpdate,
    required HandleLexicalItemRemovalLearningContentOuterUpdate
        handleLexicalItemRemovalLearningContentOuterUpdate,
    required HandleLexicalItemRemovalPremiumAccessStatusOuterUpdate
        handleLexicalItemRemovalPremiumAccessStatusOuterUpdate,
    required PronounceLexicalItem pronounceLexicalItem,
    required SelectAllLexicalItemsDuringLexicalItemRemoval
        selectAllLexicalItemsDuringLexicalItemRemoval,
    required StartCollectionDeletion startCollectionDeletion,
    required StartKnownLexicalItemSelection startKnownLexicalItemSelection,
    required StartLexicalItemRemoval startLexicalItemRemoval,
    required StartSelectedLexicalItemRemoval startSelectedLexicalItemRemoval,
    required ToggleKnownLexicalItemSelection toggleKnownLexicalItemSelection,
    required ToggleLexicalItemRemovalSelection toggleLexicalItemRemovalSelection,
  })  : _pageModel = pageModel,
        _learningContentBloc = learningContentBloc,
        _userInfoBloc = userInfoBloc,
        _overlayBloc = overlayBloc,
        _rootNavigatorBloc = rootNavigatorBloc,
        _collectionNavigatorBloc = collectionNavigatorBloc,
        _purchaseFlowDisplayBloc = purchaseFlowDisplayBloc,
        _cancelKnownLexicalItemSelection = cancelKnownLexicalItemSelection,
        _cancelLexicalItemRemoval = cancelLexicalItemRemoval,
        _completeCustomCollectionRenaming = completeCustomCollectionRenaming,
        _completeKnownLexicalItemSelection = completeKnownLexicalItemSelection,
        _completeSelectedLexicalItemRemoval = completeSelectedLexicalItemRemoval,
        _completeCollectionDeletion = completeCollectionDeletion,
        _deselectAllLexicalItemsDuringLexicalItemRemoval =
            deselectAllLexicalItemsDuringLexicalItemRemoval,
        _handleDefaultStateLearningContentOuterUpdate =
            handleDefaultStateLearningContentOuterUpdate,
        _handleDefaultStatePremiumAccessStatusOuterUpdate =
            handleDefaultStatePremiumAccessStatusOuterUpdate,
        _handleCustomCollectionRenamingLearningContentOuterUpdate =
            handleCustomCollectionRenamingLearningContentOuterUpdate,
        _handleCustomCollectionRenamingPremiumAccessStatusOuterUpdate =
            handleCustomCollectionRenamingPremiumAccessStatusOuterUpdate,
        _handleKnownLexicalItemSelectionLearningContentOuterUpdate =
            handleKnownLexicalItemSelectionLearningContentOuterUpdate,
        _handleLexicalItemRemovalLearningContentOuterUpdate =
            handleLexicalItemRemovalLearningContentOuterUpdate,
        _handleLexicalItemRemovalPremiumAccessStatusOuterUpdate =
            handleLexicalItemRemovalPremiumAccessStatusOuterUpdate,
        _pronounceLexicalItem = pronounceLexicalItem,
        _selectAllLexicalItemsDuringLexicalItemRemoval =
            selectAllLexicalItemsDuringLexicalItemRemoval,
        _startCollectionDeletion = startCollectionDeletion,
        _startKnownLexicalItemSelection = startKnownLexicalItemSelection,
        _startLexicalItemRemoval = startLexicalItemRemoval,
        _startSelectedLexicalItemRemoval = startSelectedLexicalItemRemoval,
        _toggleKnownLexicalItemSelection = toggleKnownLexicalItemSelection,
        _toggleLexicalItemRemovalSelection = toggleLexicalItemRemovalSelection,
        super(_getInitialState(
          pageModel: pageModel,
          learningContentBlocState: learningContentBloc.state!,
          userInfoBlocState: userInfoBloc.state!,
          getInitialData: getInitialData,
        )) {
    streamWithPrevious = stream.withPreviousSeeded(state);

    _learningContentBlocSubscription =
        learningContentBloc.stream.listen(_onLearningContentBlocStateChanged);

    _userInfoBlocSubscription = userInfoBloc.streamWithPrevious.listen(_onUserInfoBlocStateChanged);
  }

  final SingleCollectionPageModel _pageModel;
  final LearningContentBloc _learningContentBloc;
  final UserInfoBloc _userInfoBloc;
  final OverlayBloc _overlayBloc;
  final RootNavigatorBloc _rootNavigatorBloc;
  final CollectionNavigatorBloc _collectionNavigatorBloc;
  final PurchaseFlowDisplayBloc _purchaseFlowDisplayBloc;

  final CancelKnownLexicalItemSelection _cancelKnownLexicalItemSelection;
  final CancelLexicalItemRemoval _cancelLexicalItemRemoval;
  final CompleteCustomCollectionRenaming _completeCustomCollectionRenaming;
  final CompleteKnownLexicalItemSelection _completeKnownLexicalItemSelection;
  final CompleteSelectedLexicalItemRemoval _completeSelectedLexicalItemRemoval;
  final CompleteCollectionDeletion _completeCollectionDeletion;
  final DeselectAllLexicalItemsDuringLexicalItemRemoval
      _deselectAllLexicalItemsDuringLexicalItemRemoval;
  final HandleDefaultStateLearningContentOuterUpdate _handleDefaultStateLearningContentOuterUpdate;
  final HandleDefaultStatePremiumAccessStatusOuterUpdate
      _handleDefaultStatePremiumAccessStatusOuterUpdate;
  final HandleCustomCollectionRenamingLearningContentOuterUpdate
      _handleCustomCollectionRenamingLearningContentOuterUpdate;
  final HandleCustomCollectionRenamingPremiumAccessStatusOuterUpdate
      _handleCustomCollectionRenamingPremiumAccessStatusOuterUpdate;
  final HandleKnownLexicalItemSelectionLearningContentOuterUpdate
      _handleKnownLexicalItemSelectionLearningContentOuterUpdate;
  final HandleLexicalItemRemovalLearningContentOuterUpdate
      _handleLexicalItemRemovalLearningContentOuterUpdate;
  final HandleLexicalItemRemovalPremiumAccessStatusOuterUpdate
      _handleLexicalItemRemovalPremiumAccessStatusOuterUpdate;
  final PronounceLexicalItem _pronounceLexicalItem;
  final SelectAllLexicalItemsDuringLexicalItemRemoval
      _selectAllLexicalItemsDuringLexicalItemRemoval;
  final StartCollectionDeletion _startCollectionDeletion;
  final StartKnownLexicalItemSelection _startKnownLexicalItemSelection;
  final StartLexicalItemRemoval _startLexicalItemRemoval;
  final StartSelectedLexicalItemRemoval _startSelectedLexicalItemRemoval;
  final ToggleKnownLexicalItemSelection _toggleKnownLexicalItemSelection;
  final ToggleLexicalItemRemovalSelection _toggleLexicalItemRemovalSelection;

  late final Stream<(SingleCollectionPageBlocState, SingleCollectionPageBlocState)>
      streamWithPrevious;

  late final StreamSubscription<LearningContentBlocState> _learningContentBlocSubscription;
  late final StreamSubscription<(UserInfoBlocState, UserInfoBlocState?)> _userInfoBlocSubscription;

  static SingleCollectionPageBlocState _getInitialState({
    required SingleCollectionPageModel pageModel,
    required LearningContentBlocState learningContentBlocState,
    required UserInfoBlocState userInfoBlocState,
    required GetInitialData getInitialData,
  }) {
    final result = getInitialData(
      collectionId: pageModel.collectionId,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    return DefaultState(
      collection: result.collection,
      knownLexicalItemSelectionPermitted: result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: result.lexicalItemAdditionPermitted,
      permittedLexicalItemIds: result.permittedLexicalItemIds,
      collectionLexicalItems: result.collectionLexicalItems,
      collectionRepetitionProgresses: result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: result.unspecifiedLexicalItemExists,
      collectionLexicalItemProgressPercentMap: result.collectionLexicalItemProgressPercentMap,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
    );
  }

  void requestLexicalItemPronunciation({
    required String lexicalItemTitle,
  }) {
    _pronounceLexicalItem(
      lexicalItemTitle: lexicalItemTitle,
    );
  }

  void _onLearningContentBlocStateChanged(LearningContentBlocState learningContentBlocState) {
    if (state is DefaultState) {
      _onDefaultStateLearningContentBlocStateChanged(
        learningContentBlocState: learningContentBlocState,
      );
    } else if (state is KnownLexicalItemSelection) {
      _onKnownLexicalItemSelectionLearningContentBlocStateChanged(
        learningContentBlocState: learningContentBlocState,
      );
    } else if (state is LexicalItemRemoval) {
      _onLexicalItemRemovalLearningContentBlocStateChanged(
        learningContentBlocState: learningContentBlocState,
      );
    } else if (state is CustomCollectionRenaming) {
      _onCustomCollectionRenamingLearningContentBlocStateChanged(
        learningContentBlocState: learningContentBlocState,
      );
    }
  }

  void _onUserInfoBlocStateChanged((UserInfoBlocState, UserInfoBlocState?) valueWithPrevious) {
    final (userInfoBlocState, userInfoBlocPreviousState) = valueWithPrevious;

    if (state is DefaultState) {
      _onDefaultStateUserInfoBlocStateChanged(
        userInfoBlocState: userInfoBlocState,
        userInfoBlocPreviousState: userInfoBlocPreviousState!,
      );
    } else if (state is LexicalItemRemoval) {
      _onLexicalItemRemovalUserInfoBlocStateChanged(
        userInfoBlocState: userInfoBlocState,
      );
    } else if (state is CustomCollectionRenaming) {
      _onCustomCollectionRenamingUserInfoBlocStateChanged(
        userInfoBlocState: userInfoBlocState,
        userInfoBlocPreviousState: userInfoBlocPreviousState!,
      );
    }
  }

  void _forceClosePage() {
    const transitionInfo = RemovalNavigatorTransitionInfo(
      hasAnimation: false,
    );

    _collectionNavigatorBloc.closePage(
      pageModel: _pageModel,
      transitionInfo: transitionInfo,
    );
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [DefaultState]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onDefaultStateLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as DefaultState;

    if (_state.lexicalItemMap == learningContentBlocState.lexicalItemMap &&
        _state.collectionListItems == learningContentBlocState.collectionListItems) {
      return;
    }

    final failureOrResult = _handleDefaultStateLearningContentOuterUpdate(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case NonexistentCollectionDefaultStateLearningContentOuterUpdateHandlingFailure():
          _forceClosePage();
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collection: () => result.collection,
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
      collectionLexicalItems: () => result.collectionLexicalItems,
      collectionRepetitionProgresses: () => result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: () => result.unspecifiedLexicalItemExists,
      collectionLexicalItemProgressPercentMap: () => result.collectionLexicalItemProgressPercentMap,
    ));
  }

  void _onDefaultStateUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as DefaultState;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleDefaultStatePremiumAccessStatusOuterUpdate(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      knownLexicalItemSelectionPermitted: () => result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: () => result.lexicalItemAdditionPermitted,
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
    ));
  }

  void requestCustomCollectionRenamingStart() {
    final _state = state;

    if (_state is! DefaultState ||
        _state.collectionExplicitlyDeleted ||
        _state.collection is! CustomCollection) {
      return;
    }

    final collection = _state.collection as CustomCollection;

    emit(CustomCollectionRenaming(
      collection: collection,
      collectionLexicalItems: _state.collectionLexicalItems,
      knownLexicalItemSelectionPermitted: _state.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: _state.lexicalItemAdditionPermitted,
      collectionRepetitionProgresses: _state.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: _state.unspecifiedLexicalItemExists,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
    ));
  }

  void requestKnownLexicalItemSelectionStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionExplicitlyDeleted) {
      return;
    }

    if (!_state.knownLexicalItemSelectionPermitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _startKnownLexicalItemSelection(
      collection: _state.collection,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(KnownLexicalItemSelection(
      unspecifiedLexicalItems: result.unspecifiedLexicalItems,
      selectedLexicalItemIds: result.selectedLexicalItemIds,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
    ));
  }

  void requestLexicalItemRemovalStart() {
    final _state = state;

    if (_state is! DefaultState ||
        _state.collectionExplicitlyDeleted ||
        _state.collection is! CustomCollection) {
      return;
    }

    final result = _startLexicalItemRemoval();

    emit(LexicalItemRemoval(
      selectedLexicalItemIds: result.selectedLexicalItemIds,
      collectionLexicalItems: _state.collectionLexicalItems,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
    ));
  }

  void requestCollectionDeletionStart() {
    final _state = state;

    if (_state is! DefaultState ||
        _state.collectionExplicitlyDeleted ||
        _state.collection is! CustomCollection) {
      return;
    }

    final result = _startCollectionDeletion(
      collectionLexicalItems: _state.collectionLexicalItems,
    );

    final collectionDeletionDialogStatusInfo = CollectionDeletionDialogStatusInfo(
      shown: true,
      deletionModes: result.deletionModes,
    );

    emit(_state.copyWith(
      collectionDeletionDialogStatusInfo: () => collectionDeletionDialogStatusInfo,
    ));
  }

  void requestCollectionDeletion({
    required CollectionDeletionMode deletionMode,
  }) {
    final _state = state;

    if (_state is! DefaultState ||
        _state.collectionExplicitlyDeleted ||
        _state.collection is! CustomCollection) {
      return;
    }

    final collection = _state.collection as CustomCollection;

    final failureOrResult = _completeCollectionDeletion(
      collection: collection,
      deletionMode: deletionMode,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case CustomLexicalItemNotExclusiveCollectionDeletionCompletionFailure():
          emit(_state.copyWith(
            customLexicalItemDeletionDialogShown: () => true,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    _collectionNavigatorBloc.handlePop();
    _overlayBloc.showSuccessIndicator();

    emit(_state.copyWith(
      collectionExplicitlyDeleted: () => true,
    ));

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestLexicalItemAdditionPageOpening() {
    final _state = state;

    if (_state is! DefaultState ||
        _state.collectionExplicitlyDeleted ||
        _state.collection is! CustomCollection) {
      return;
    }

    if (!_state.lexicalItemAdditionPermitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final pageModel = LexicalItemAdditionPageModel(
      customCollectionId: _pageModel.collectionId,
    );

    _rootNavigatorBloc.pushPage(
      pageModel: pageModel,
    );
  }

  void requestCollectionLexicalItemPageOpening({
    required String lexicalItemId,
  }) {
    final _state = state;

    if (_state is! DefaultState || _state.collectionExplicitlyDeleted) {
      return;
    }

    final pageModel = CollectionSingleLexicalItemPageModel(
      lexicalItemId: lexicalItemId,
      collectionId: _pageModel.collectionId,
    );

    _collectionNavigatorBloc.pushPage(
      pageModel: pageModel,
    );
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [KnownLexicalItemSelection]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onKnownLexicalItemSelectionLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as KnownLexicalItemSelection;

    final failureOrResult = _handleKnownLexicalItemSelectionLearningContentOuterUpdate(
      collectionId: _pageModel.collectionId,
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionKnownLexicalItemSelectionLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      if (failure
          is NonexistentUnspecifiedLexicalItemKnownLexicalItemSelectionLearningContentOuterUpdateHandlingFailure) {
        emit(DefaultState(
          collection: failure.collection,
          collectionLexicalItems: failure.collectionLexicalItems,
          knownLexicalItemSelectionPermitted: failure.knownLexicalItemSelectionPermitted,
          lexicalItemAdditionPermitted: failure.lexicalItemAdditionPermitted,
          collectionLexicalItemProgressPercentMap: failure.collectionLexicalItemProgressPercentMap,
          permittedLexicalItemIds: failure.permittedLexicalItemIds,
          collectionRepetitionProgresses: failure.collectionRepetitionProgresses,
          unspecifiedLexicalItemExists: failure.unspecifiedLexicalItemExists,
          collectionExplicitlyDeleted: false,
          lexicalItemMap: learningContentBlocState.lexicalItemMap,
          collectionListItems: learningContentBlocState.collectionListItems,
        ));

        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionLexicalItemProgressPercentMap: () => result.collectionLexicalItemProgressPercentMap,
      unspecifiedLexicalItems: () => result.unspecifiedLexicalItems,
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  void requestKnownLexicalItemSelectionCancellation() {
    final _state = state;

    if (_state is! KnownLexicalItemSelection) {
      return;
    }

    final result = _cancelKnownLexicalItemSelection(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      unspecifiedLexicalItems: _state.unspecifiedLexicalItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    emit(DefaultState(
      collection: result.collection,
      collectionLexicalItems: result.collectionLexicalItems,
      knownLexicalItemSelectionPermitted: result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: result.lexicalItemAdditionPermitted,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: result.permittedLexicalItemIds,
      collectionRepetitionProgresses: result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: result.unspecifiedLexicalItemExists,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestKnownLexicalItemSelectionCompletion() async {
    final _state = state;

    if (_state is! KnownLexicalItemSelection) {
      return;
    }

    final result = _completeKnownLexicalItemSelection(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      selectionLexicalItemIds: _state.selectedLexicalItemIds,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    emit(DefaultState(
      collection: result.collection,
      collectionLexicalItems: result.collectionLexicalItems,
      knownLexicalItemSelectionPermitted: result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: result.lexicalItemAdditionPermitted,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: result.permittedLexicalItemIds,
      collectionRepetitionProgresses: result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: result.unspecifiedLexicalItemExists,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: result.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestKnownLexicalItemSelectionToggling({
    required String lexicalItemId,
  }) {
    final _state = state;

    if (_state is! KnownLexicalItemSelection) {
      return;
    }

    final result = _toggleKnownLexicalItemSelection(
      lexicalItemId: lexicalItemId,
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
    );

    emit(_state.copyWith(
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [LexicalItemRemoval]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onLexicalItemRemovalLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as LexicalItemRemoval;

    final failureOrResult = _handleLexicalItemRemovalLearningContentOuterUpdate(
      collectionId: _pageModel.collectionId,
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionLexicalItemRemovalLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      if (failure
          is NonexistentUnspecifiedLexicalItemLexicalItemRemovalLearningContentOuterUpdateHandlingFailure) {
        emit(DefaultState(
          collection: failure.collection,
          collectionLexicalItems: failure.collectionLexicalItems,
          knownLexicalItemSelectionPermitted: failure.knownLexicalItemSelectionPermitted,
          lexicalItemAdditionPermitted: failure.lexicalItemAdditionPermitted,
          collectionLexicalItemProgressPercentMap: failure.collectionLexicalItemProgressPercentMap,
          permittedLexicalItemIds: failure.permittedLexicalItemIds,
          collectionRepetitionProgresses: failure.collectionRepetitionProgresses,
          unspecifiedLexicalItemExists: failure.unspecifiedLexicalItemExists,
          collectionExplicitlyDeleted: false,
          lexicalItemMap: learningContentBlocState.lexicalItemMap,
          collectionListItems: learningContentBlocState.collectionListItems,
        ));

        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionLexicalItemProgressPercentMap: () => result.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
      collectionLexicalItems: () => result.collectionLexicalItems,
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  void _onLexicalItemRemovalUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
  }) {
    final _state = state as LexicalItemRemoval;

    final result = _handleLexicalItemRemovalPremiumAccessStatusOuterUpdate(
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
    ));
  }

  void requestLexicalItemRemovalCancellation() {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final result = _cancelLexicalItemRemoval(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    emit(DefaultState(
      collection: result.collection,
      collectionLexicalItems: result.collectionLexicalItems,
      knownLexicalItemSelectionPermitted: result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: result.lexicalItemAdditionPermitted,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
      collectionRepetitionProgresses: result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: result.unspecifiedLexicalItemExists,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestSelectedLexicalItemRemovalStart() {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final result = _startSelectedLexicalItemRemoval(
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    final removalDialogStatusInfo = CollectionLexicalItemRemovalDialogStatusInfo(
      shown: true,
      removalModes: result.removalModes,
    );

    emit(_state.copyWith(
      lexicalItemRemovalDialogStatusInfo: () => removalDialogStatusInfo,
    ));
  }

  void requestSelectedLexicalItemRemovalCompletion({
    required CollectionLexicalItemRemovalMode removalMode,
  }) {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final failureOrResult = _completeSelectedLexicalItemRemoval(
      collectionId: _pageModel.collectionId,
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
      removalMode: removalMode,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case CustomLexicalItemNotExclusiveSelectedLexicalItemRemovalCompletionFailure():
          emit(_state.copyWith(
            customLexicalItemDeletionDialogShown: () => true,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(DefaultState(
      collection: result.collection,
      collectionLexicalItems: result.collectionLexicalItems,
      knownLexicalItemSelectionPermitted: result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: result.lexicalItemAdditionPermitted,
      collectionLexicalItemProgressPercentMap: result.collectionLexicalItemProgressPercentMap,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
      collectionRepetitionProgresses: result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: result.unspecifiedLexicalItemExists,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: result.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestLexicalItemRemovalSelectionToggling({
    required String lexicalItemId,
  }) {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final result = _toggleLexicalItemRemovalSelection(
      lexicalItemId: lexicalItemId,
      selectedLexicalItemIds: _state.selectedLexicalItemIds,
    );

    emit(_state.copyWith(
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  void requestLexicalItemRemovalAllLexicalItemSelection() {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final result = _selectAllLexicalItemsDuringLexicalItemRemoval(
      collectionLexicalItems: _state.collectionLexicalItems,
    );

    emit(_state.copyWith(
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  void requestLexicalItemRemovalAllLexicalItemDeselection() {
    final _state = state;

    if (_state is! LexicalItemRemoval) {
      return;
    }

    final result = _deselectAllLexicalItemsDuringLexicalItemRemoval();

    emit(_state.copyWith(
      selectedLexicalItemIds: () => result.selectedLexicalItemIds,
    ));
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [CustomCollectionRenaming]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onCustomCollectionRenamingLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as CustomCollectionRenaming;

    final failureOrResult = _handleCustomCollectionRenamingLearningContentOuterUpdate(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionCustomCollectionRenamingLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collection: () => result.collection,
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
      collectionLexicalItems: () => result.collectionLexicalItems,
      collectionRepetitionProgresses: () => result.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: () => result.unspecifiedLexicalItemExists,
      collectionLexicalItemProgressPercentMap: () => result.collectionLexicalItemProgressPercentMap,
    ));
  }

  void _onCustomCollectionRenamingUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as CustomCollectionRenaming;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleCustomCollectionRenamingPremiumAccessStatusOuterUpdate(
      collectionId: _pageModel.collectionId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      knownLexicalItemSelectionPermitted: () => result.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: () => result.lexicalItemAdditionPermitted,
      permittedLexicalItemIds: () => result.permittedLexicalItemIds,
    ));
  }

  void cancelCustomCollectionRenaming() {
    final _state = state;

    if (_state is! CustomCollectionRenaming) {
      return;
    }

    emit(DefaultState(
      collection: _state.collection,
      knownLexicalItemSelectionPermitted: _state.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: _state.lexicalItemAdditionPermitted,
      collectionLexicalItems: _state.collectionLexicalItems,
      collectionRepetitionProgresses: _state.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: _state.unspecifiedLexicalItemExists,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestCustomCollectionRenamingCompletion({
    required String collectionTitleInput,
  }) {
    final _state = state;

    if (_state is! CustomCollectionRenaming) {
      return;
    }

    final failureOrResult = _completeCustomCollectionRenaming(
      collection: _state.collection,
      collectionTitleInput: collectionTitleInput,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case InvalidCollectionTitleCustomCollectionRenamingCompletionFailure():
          emit(_state.copyWith(
            validationError: () => failure.validationError,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(DefaultState(
      collection: result.collection,
      knownLexicalItemSelectionPermitted: _state.knownLexicalItemSelectionPermitted,
      lexicalItemAdditionPermitted: _state.lexicalItemAdditionPermitted,
      collectionLexicalItems: _state.collectionLexicalItems,
      collectionRepetitionProgresses: _state.collectionRepetitionProgresses,
      unspecifiedLexicalItemExists: _state.unspecifiedLexicalItemExists,
      permittedLexicalItemIds: _state.permittedLexicalItemIds,
      collectionLexicalItemProgressPercentMap: _state.collectionLexicalItemProgressPercentMap,
      collectionExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      collectionListItems: () => result.collectionListItems,
    );
  }

  // </editor-fold>

  @override
  Future<void> close() {
    _learningContentBlocSubscription.cancel();
    _userInfoBlocSubscription.cancel();
    return super.close();
  }
}
